package com.tianji.learning.service.impl;

import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.learning.LearningLessonDTO;
import com.tianji.api.dto.learning.LearningRecordDTO;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningRecordFormDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.SectionType;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.mq.message.SignInMessage;
import com.tianji.learning.service.ILearningLessonService;
import com.tianji.learning.service.ILearningRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.utils.LearningRecordDelayTaskHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * <p>
 * 学习记录表 服务实现类
 * </p>
 *
 * @author mxl
 * @since 2024-06-25
 */
@Service
@RequiredArgsConstructor
public class LearningRecordServiceImpl extends ServiceImpl<LearningRecordMapper, LearningRecord> implements ILearningRecordService {

    private final ILearningLessonService learningLessonService;

    private final CourseClient courseClient;

    private final LearningRecordDelayTaskHandler taskHandler;

    private final RabbitMqHelper mqHelper;

    @Override
    public LearningLessonDTO queryLearningRecordByCourse(Long courseId) {
        // 1. 获取当前用户
        Long user = UserContext.getUser();
        // 2. 查询课表信息
        LearningLesson lesson = learningLessonService.lambdaQuery()
                .eq(LearningLesson::getUserId, user)
                .eq(LearningLesson::getCourseId, courseId).one();

        // 3. 查询学习记录
        List<LearningRecord> records = this.lambdaQuery()
                .eq(LearningRecord::getLessonId, lesson.getId())
                .list();
        // 4. 封装DTO
        LearningLessonDTO dto = new LearningLessonDTO();
        dto.setId(lesson.getId());    // 课表id
        dto.setLatestSectionId(lesson.getLatestSectionId());  // 最近学习的课程
        List<LearningRecordDTO> learningRecordDTOS = BeanUtils.copyList(records, LearningRecordDTO.class);
        dto.setRecords(learningRecordDTOS);   // 每节播放进度
        return dto;
    }

    @Override
    public void addLearningRecord(LearningRecordFormDTO formDTO) {
        // 1. 获取当前用户
        Long user = UserContext.getUser();
        // 2. 处理学习记录
        boolean isFinished = false;
        if(formDTO.getSectionType().equals(SectionType.VIDEO)){
            // 2.1 视频记录
            isFinished = handleVideoRecord(user, formDTO);
        }else{
            // 2.2 考试记录
            isFinished = handleExameRecord(user, formDTO);
        }

        if(!isFinished) return ;

        // 3. 课表记录
        handleLessonData(formDTO, isFinished);
        // 4. 如果是第一次完成需要想mq发消息存储
        if(isFinished){
            mqHelper.send(MqConstants.Exchange.LEARNING_EXCHANGE,
                    MqConstants.Key.LEARN_SECTION,
                    SignInMessage.of(user, 10));
        }
    }

    private void handleLessonData(LearningRecordFormDTO formDTO, boolean isFinished) {
        // 1. 查询课表数据
        LearningLesson lesson = learningLessonService.getById(formDTO.getLessonId());
        if(lesson == null){
            throw new BizIllegalException("课表不存在");
        }

        // 2. 更新课表信息
        //2.1 判断是否第一次学完
        boolean allFinished = false;
        if(isFinished){
            CourseFullInfoDTO cInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
            if(cInfo == null){
                throw new BizIllegalException("课程不存在");
            }
            Integer sectionNum = cInfo.getSectionNum();
            Integer learnedSections = lesson.getLearnedSections();
            allFinished = (learnedSections + 1) >= sectionNum;
        }
        //2.2 更新数据库  课程是否都完成了  最近学习的课程id等等
        learningLessonService.lambdaUpdate()
                .set((lesson.getStatus() == LessonStatus.NOT_BEGIN) && !allFinished, LearningLesson::getStatus, LessonStatus.LEARNING)
                .set(allFinished, LearningLesson::getStatus, LessonStatus.FINISHED)
                .set(LearningLesson::getLatestLearnTime, formDTO.getCommitTime())
                .set(LearningLesson::getLatestSectionId, formDTO.getSectionId())
                .setSql(isFinished, "learned_sections = learned_sections + 1")
                .eq(LearningLesson::getId, lesson.getId());
    }

    private boolean handleVideoRecord(Long user, LearningRecordFormDTO formDTO) {
        // 1. 查寻旧表
        LearningRecord record = queryOldRecord(formDTO.getSectionId(), formDTO.getLessonId());

//        旧版从数据库查询
//        LearningRecord record = this.lambdaQuery()
//                .eq(LearningRecord::getLessonId, formDTO.getLessonId())
//                .eq(LearningRecord::getSectionId, formDTO.getSectionId()).one();

        // 2. 判断是否存在
        if(record == null){
            // 3. 如果不存在新增
            LearningRecord learningRecord = BeanUtils.copyBean(formDTO, LearningRecord.class);
            learningRecord.setUserId(user);
            boolean save = this.save(learningRecord);
            if(!save){
                throw new DbException("更新视频记录失败");
            }
            return false;
        }

        // 4. 存在则更新
        // 判断是否为第一次学完
        boolean isFinished = !record.getFinished() && (formDTO.getMoment() * 2 >= formDTO.getDuration());
        if(isFinished){
            // 4.1 学完了 更新db 清空cache
            boolean update = this.lambdaUpdate()
                    .set(LearningRecord::getMoment, formDTO.getMoment())
                    .set(isFinished, LearningRecord::getFinished, true)
                    .set(isFinished, LearningRecord::getFinishTime, formDTO.getCommitTime())
                    .eq(LearningRecord::getId, record.getId())
                    .update();
            if(!update){
                throw new DbException("更新视频记录失败");
            }
            taskHandler.cleanRecordCache(formDTO.getLessonId(), formDTO.getSectionId());
        }else{
            // 4.2 没学完  更新cache 提交延迟任务
            LearningRecord learningRecord = new LearningRecord();
            learningRecord.setMoment(formDTO.getMoment());
            learningRecord.setId(record.getId());
            learningRecord.setSectionId(formDTO.getSectionId());
            learningRecord.setLessonId(formDTO.getLessonId());
            learningRecord.setFinished(record.getFinished());
            taskHandler.addLearningRecordTask(learningRecord);
        }
        // 旧版代码
//        boolean update = this.lambdaUpdate()
//                .set(LearningRecord::getMoment, formDTO.getMoment())
//                .set(isFinished, LearningRecord::getFinished, true)
//                .set(isFinished, LearningRecord::getFinishTime, formDTO.getCommitTime())
//                .eq(LearningRecord::getId, record.getId())
//                .update();
//        if(!update){
//            throw new DbException("更新视频记录失败");
//        }
        return isFinished;
    }

    private LearningRecord queryOldRecord(Long sectionId, Long lessonId) {
        // 1. 查询缓存
        LearningRecord learningRecord = taskHandler.readRecordCache(lessonId, sectionId);

        ///2.如果命中返回
        if(learningRecord != null) return learningRecord;
        // 3.为命中查询DB
        LearningRecord dbRecord = this.lambdaQuery()
                .eq(LearningRecord::getLessonId, lessonId)
                .eq(LearningRecord::getSectionId, sectionId).one();
        // 3.1 未命中返回null准备插入数据库
        if(dbRecord == null) return null;
        // 4.DB命中更新缓存
        taskHandler.addLearningRecordTask(dbRecord);
        return dbRecord;
    }

    private boolean handleExameRecord(Long user, LearningRecordFormDTO formDTO) {
        //1. 将DTO转换为PO
        LearningRecord learningRecord = BeanUtils.copyBean(formDTO, LearningRecord.class);
        learningRecord.setUserId(user);
        learningRecord.setFinished(true);
        learningRecord.setFinishTime(formDTO.getCommitTime());
        boolean save = this.save(learningRecord);
        if(!save){
            throw new DbException("新增考试记录失败");
        }
        return true;
    }
}
